package response

import (
	"encoding/binary"
	"github.com/adwindone/sdk-go/model/blockchain"
	"github.com/adwindone/sdk-go/util/hexutil"
)

type RawTransaction struct {
	// Types that are assignable to Tx:
	//
	//	*RawTransaction_NormalTx
	//	*RawTransaction_UtxoTx
	Tx isRawTransaction_Tx `json:"tx"`
}

type isRawTransaction_Tx interface {
	isRawTransaction_Tx()
}

type RawTransaction_NormalTx struct {
	NormalTx *UnverifiedTransaction `json:"normal_tx"`
}

type RawTransaction_UtxoTx struct {
	UtxoTx *UnverifiedUtxoTransaction `json:"utxo_tx"`
}

func (*RawTransaction_NormalTx) isRawTransaction_Tx() {}

func (*RawTransaction_UtxoTx) isRawTransaction_Tx() {}

type UnverifiedTransaction struct {
	Transaction     *Transaction `json:"transaction"`
	TransactionHash string       `json:"transaction_hash"`
	Witness         *Witness     `json:"witness"`
}

type UnverifiedUtxoTransaction struct {
	Transaction     *UtxoTransaction `json:"transaction"`
	TransactionHash string           `json:"transaction_hash"`
	Witnesses       []*Witness       `json:"witnesses"`
}

type Transaction struct {
	Nonce           string `json:"nonce"`
	To              string `json:"to"`
	Data            string `json:"data"`
	ChainId         string `json:"chain_id"`
	ValidUntilBlock uint64 `json:"valid_until_block"`
	Quota           uint64 `json:"quota"`
	Value           uint32 `json:"value"`
	Version         uint32 `json:"version"`
}

type UtxoTransaction struct {
	PreTxHash string `json:"pre_tx_hash"`
	Output    []byte `json:"output"`
	LockId    uint64 `json:"lock_id"`
	Version   uint32 `json:"version"`
}

type Witness struct {
	Sender    string `json:"sender"`
	Signature string `json:"signature"`
}

func RawTransactionConvert(rawTx *blockchain.RawTransaction) *RawTransaction {
	if rawTx == nil {
		return nil
	}
	resp := new(RawTransaction)
	if rawTx.GetNormalTx() != nil {
		resp.Tx = RawTransaction_NormalTxConvert(rawTx.GetNormalTx())
	} else if rawTx.GetUtxoTx() != nil {
		resp.Tx = RawTransaction_UtxoTxConvert(rawTx.GetUtxoTx())
	}
	return resp
}

func RawTransaction_NormalTxConvert(normalTx *blockchain.UnverifiedTransaction) *RawTransaction_NormalTx {
	if normalTx == nil {
		return nil
	}
	resp := new(RawTransaction_NormalTx)
	resp.NormalTx = UnverifiedTransactionConvert(normalTx)
	return resp
}

func RawTransaction_UtxoTxConvert(utxoTx *blockchain.UnverifiedUtxoTransaction) *RawTransaction_UtxoTx {
	if utxoTx == nil {
		return nil
	}
	resp := new(RawTransaction_UtxoTx)
	resp.UtxoTx = UnverifiedUtxoTransactionConvert(utxoTx)
	return resp
}

func UnverifiedTransactionConvert(unverifiedTx *blockchain.UnverifiedTransaction) *UnverifiedTransaction {
	if unverifiedTx == nil {
		return nil
	}
	resp := new(UnverifiedTransaction)
	resp.Transaction = TransactionConvert(unverifiedTx.GetTransaction())
	resp.TransactionHash = hexutil.Encode(unverifiedTx.TransactionHash)
	resp.Witness = WitnessConvert(unverifiedTx.GetWitness())
	return resp
}

func UnverifiedUtxoTransactionConvert(unverifiedUtxoTx *blockchain.UnverifiedUtxoTransaction) *UnverifiedUtxoTransaction {
	if unverifiedUtxoTx == nil {
		return nil
	}
	resp := new(UnverifiedUtxoTransaction)
	resp.Transaction = UtxoTransactionConvert(unverifiedUtxoTx.GetTransaction())
	resp.TransactionHash = hexutil.Encode(unverifiedUtxoTx.TransactionHash)
	resp.Witnesses = WitnessConverts(unverifiedUtxoTx.GetWitnesses())
	return resp
}

func TransactionConvert(tx *blockchain.Transaction) *Transaction {
	if tx == nil {
		return nil
	}
	resp := new(Transaction)
	resp.Nonce = tx.Nonce
	resp.To = hexutil.Encode(tx.To)
	resp.Data = hexutil.Encode(tx.Data)
	resp.ChainId = hexutil.Encode(tx.ChainId)
	resp.ValidUntilBlock = tx.ValidUntilBlock
	resp.Quota = tx.Quota
	resp.Value = binary.BigEndian.Uint32(tx.Value)
	resp.Version = tx.Version
	return resp
}

func UtxoTransactionConvert(utxoTx *blockchain.UtxoTransaction) *UtxoTransaction {
	if utxoTx == nil {
		return nil
	}
	resp := new(UtxoTransaction)
	resp.PreTxHash = hexutil.Encode(utxoTx.PreTxHash)
	resp.Output = utxoTx.Output
	resp.LockId = utxoTx.LockId
	resp.Version = utxoTx.Version
	return resp
}

func WitnessConvert(witness *blockchain.Witness) *Witness {
	if witness == nil {
		return nil
	}
	resp := new(Witness)
	resp.Sender = hexutil.Encode(witness.Sender)
	resp.Signature = hexutil.Encode(witness.Signature)
	return resp
}

func WitnessConverts(witnesses []*blockchain.Witness) []*Witness {
	if witnesses == nil {
		return nil
	}
	var result []*Witness
	for _, witness := range witnesses {
		result = append(result, WitnessConvert(witness))
	}
	return result
}
